package com.wsx.ones.monitor.core.loader;

import com.google.common.util.concurrent.*;
import com.wsx.ones.monitor.common.util.FinalStaticUtil;
import com.wsx.ones.monitor.core.common.ChannelState;
import com.wsx.ones.monitor.core.exec.push.PushInitTask;
import com.wsx.ones.monitor.core.executer.RpcExecuter;
import com.wsx.ones.monitor.core.netty.PushMessageHandler;
import com.wsx.ones.monitor.core.serialize.ProtocolSelecter;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;

import java.net.InetSocketAddress;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 客服端的服务加载器，实现初始化netty客户端的核心类
 * 该类的核心实现的load方法，该方法是加载对应的协议，ip
 * Created by wangshuaixin on 17/2/4.
 */
public class RpcClientLoader extends RpcServerBase {

    //单例模式的设计
    private volatile static RpcClientLoader rpcClientLoader;

    //执行的线程池操作
    private static ListeningExecutorService threadPoolExecuter = MoreExecutors.listeningDecorator(RpcExecuter.getExecuter(10, 50, -1));

    private EventLoopGroup work = new NioEventLoopGroup(PROCESS);

    //发送消息的句柄
    private PushMessageHandler pushMessageHandler;

    //单例操作涉及到数据安全采用锁机制
    private ReentrantLock lock = new ReentrantLock();
    private Condition connect = lock.newCondition();
    private Condition push = lock.newCondition();

    /**
     * 单例构造函数
     */
    private RpcClientLoader() {
    }

    /**
     * 单例提供对外的访问变量
     * @return
     */
    public static RpcClientLoader getInstance() {
        return RpcClientLoaderHelp.rpcClientLoader;
    }

    /**
     * 内部类构造安全的单例模式
     */
    private static class RpcClientLoaderHelp {
        private static RpcClientLoader rpcClientLoader = new RpcClientLoader();
    }

    /**
     * 加载对应的ip和协议
     * @param inetAddress
     */
    public synchronized void init(String inetAddress) {
        String[] addrs = inetAddress.split(FinalStaticUtil.DELIMITER);
        if (addrs.length == 3) {
            //设置当前的通信协议
            setRpcSerializeProtocol(ProtocolSelecter.getProtocol(addrs[0]));
            String host = addrs[1];
            int port = Integer.valueOf(addrs[2]);

            //netty链接的时候需要该对象
            InetSocketAddress serviceAddr = new InetSocketAddress(host, port);

            //采用google提供的guava框架中的线程池操作类
            //PushInitTask类是主要设计netty客户端连接和相关netty句柄的操作实现类
            ListenableFuture<Boolean> future = threadPoolExecuter.submit(new PushInitTask(work, serviceAddr, rpcSerializeProtocol));

            //对连接结果进行控制，guava框架中的内容
            Futures.addCallback(future, new FutureCallback<Boolean>() {
                //连接成功后的处理
                //成功的核心处理就是把发送消息的句柄传递回来，供以后客户端发送消息使用
                @Override
                public void onSuccess(Boolean result) {
                    try {
                        lock.lock();

                        if (null == pushMessageHandler) {
                            push.await();
                        }

                        if (result && null != pushMessageHandler) {
                            connect.signalAll();
                        }

                    } catch (Exception e) {

                    } finally {
                        lock.unlock();
                    }
                }
                //连接失败后的处理
                @Override
                public void onFailure(Throwable t) {
                    // TODO

                }
            }, threadPoolExecuter);
        }
    }

    /**
     * 在netty的执行过程中设置相应的服务器状态
     * @param channelState
     */
    public void setChannelState(ChannelState channelState) {
        state = channelState;
    }

    /**
     *
     */
    public synchronized void close() {
        pushMessageHandler.close();
        threadPoolExecuter.shutdown();
        work.shutdownGracefully();
        //设置服务器状态
        state = ChannelState.CLOSED;
    }

    /**
     * 对外提供netty发送消息的句柄的访问，保证数据安全，并且保证该句柄必须存在
     * @return
     * @throws InterruptedException
     */
    public PushMessageHandler getPushMessageHandler() throws InterruptedException {
        try {
            lock.lock();
            //如果发送消息的句柄还没有设置则还没有连接上服务端
            if (null == pushMessageHandler) {
                connect.await();
            }
            return pushMessageHandler;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 设计netty的客户端消息发送句柄，该方法主要提供给netty链接成功后的操作
     * @param pushMessageHandler
     */
    public void setPushMessageHandler(PushMessageHandler pushMessageHandler) {
        try {
            lock.lock();
            this.pushMessageHandler = pushMessageHandler;
            //该方法是在client连接到server后的操作，所以是连接成功，通知可以发送消息了
            push.signalAll();
        } finally {
            lock.unlock();
        }

    }
}
